package render.graph
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import render.NTData;
	import render.Triple;
	
	/**
	 * Dispatched to the change event
	 */
	[Event(name="changed",type="flash.event.Event")]
	public class Graph extends EventDispatcher
	{
		private var _rdfData:NTData;
		private var _edgeArray:Array;
		private var _nodeArray:Array;
		private var _changed:Boolean;
		private var _showAllNodes:Boolean = false;
		
		private var _existedEntityNodes:Object;
		private var _existedNodes:Object;
		private var _existedNodeCount:int;
		private var _existedEdges:Object;
		private var _mustShow:Object ;
		
		private var tmpNodes:Object;
		private var tmpEdges:Object;		
		
		public static const CHANGE:String = "change";
		
		public function Graph(rdf:NTData,showAllnodes:Boolean)
		{
			_rdfData = rdf;
			_showAllNodes = showAllnodes;
			initGraph();
		}
		public function set showAllNodes(s:Boolean):void
		{
			_showAllNodes = s;
		}
		private function mustShow(node:Node):Boolean
		{
			return _mustShow[node.id] != null;
		}
		public function addMustShow(node:Node):void
		{
			_mustShow[node.id] = true;
		}
		public function getMustShow(node:Node):Boolean
		{
			return _mustShow[node.id];
		}
		public function hideAttr(node:Node):void
		{
			tmpEdges = new Object();
			tmpNodes = new Object();
			var neighbors:Object = _existedEdges[node.id];
			for each(var edge:Edge in neighbors){
				if(edge.fromNode == node && edge.toNode.display.visible == false
					&& !mustShow(edge.toNode) ){
					tmpEdges[edge.id] = edge;
					tmpNodes[edge.toNode.id] = edge.toNode;	
					edge.toNode.display.visible = false;	
					edge.display.visible = false;			
				}
				else if(edge.toNode == node && edge.fromNode.display.visible == false
					&& !mustShow(edge.fromNode) ){
					tmpEdges[edge.id] = edge;
					tmpNodes[edge.fromNode.id] = edge.fromNode;
					edge.fromNode.display.visible = false;
					edge.display.visible = false;		
				}
			}
			_nodeArray = _nodeArray.filter(filterNode);
			_edgeArray = _edgeArray.filter(filterEdge);
		}
		private function filterNode(element:*,index:int,arr:Array):Boolean
		{
			return tmpNodes[element.id] == null;			
		}
		private function filterEdge(element:*,index:int,arr:Array):Boolean
		{
			return tmpEdges[element.id] == null;
		}
		public function showAttr(node:Node):void
		{
			var neighbors:Object = _existedEdges[node.id];
			for each(var edge:Edge in neighbors){
				if(edge.fromNode == node && edge.toNode.display.visible == false){
					_nodeArray.push(edge.toNode);
					_edgeArray.push(edge);
					edge.toNode.display.visible = true;
				}
				else if(edge.toNode == node && edge.fromNode.display.visible == false){
					_nodeArray.push(edge.fromNode);
					_edgeArray.push(edge);
					edge.fromNode.display.visible = true;
				}
			}
		}
		public function get allNodes():Object{
			return _existedNodes;
		}
		public function initGraph():void
		{
			_existedEntityNodes = new Object();
			_existedEdges = new Object();
			_existedNodeCount = 0;
			_existedNodes = new Object();
			_mustShow = new Object();
			
			_edgeArray = new Array();
			_nodeArray = new Array();
			
			var subjects:Object = new Object();
			for each(var triple:Triple in _rdfData.triples){
				var pre:String = triple.predicate;
				var obj:String = triple.object;
				var sub:String = triple.subject;
				
				subjects[sub] = sub;
				
				var subNode:Node = getNode(sub);
				var edge:Edge;
				var objNode:Node;
				
				if(triple.predicateType() == Triple.ATTR){
					objNode = createNode(obj);
					objNode.type = Node.ATTR;
				}
				else if(triple.predicateType() == Triple.REL){
					objNode = getNode(obj);
				}

				edge = new Edge(objNode,subNode,triple.predicateType() == Triple.REL,pre);
				
				addEdge(subNode,objNode,edge);
				
				if(triple.mustShow){
					addMustShow(subNode);
					addMustShow(objNode);
				}
				
			}
			
			for each(var node:Node in _existedEntityNodes){
				if( subjects[node.text] )
					node.solid();
			}
			
			changed();
		}
		public function addTriples(triples:Array):Array
		{
			var start:int = _existedNodeCount;
			for each(var triple:Triple in triples){
				var pre:String = triple.predicate;
				var obj:String = triple.object;
				var sub:String = triple.subject;
				
				var subNode:Node = getNode(sub);
				var edge:Edge;
				var objNode:Node;
				
				
				if(triple.predicateType() == Triple.ATTR){
					objNode = createNode(obj);
					objNode.type = Node.ATTR;
				}
				else if(triple.predicateType() == Triple.REL){
					objNode = getNode(obj);
				}

				var edge:Edge = new Edge(objNode,subNode,triple.predicateType() == Triple.REL,pre);
				
				// skip existed edge
				if( _existedEdges[subNode.id][objNode.id] != null &&
					_existedEdges[objNode.id][subNode.id] != null )
					continue;
				
				addEdge(subNode,objNode,edge);
				
			}
			var end:int = _existedNodeCount;
			var r:Array = new Array();
			for( var i:int = start ; i < end; i++) {
				r.push(_existedNodes[i]);
			}
			changed();
			return r;
		}
		public function addEdge(a:Node,b:Node,edge:Edge):void
		{
			var neighborsA:Object = _existedEdges[a.id];
			neighborsA[b.id] = edge;
			
			var neighborsB:Object = _existedEdges[b.id];
			neighborsB[a.id] = edge;
			
			if(_showAllNodes || (edge.isRel) )
				edgeArray.push(edge);
			
			changed();
		}
		public function removeEdge(a:Node,b:Node):void
		{
			var neighborsA:Object = _existedEdges[a.id]
			delete neighborsA[b.id];
			
			var neighborsB:Object = _existedEdges[b.id]
			delete neighborsB[a.id];
			
			changed();
		}
		public function getEdges(id:int):Object
		{
			return _existedEdges[id];	
		}
		public function getNeighbors(id:int):Array
		{
			var r:Array = new Array();
			for each(var edge:Edge in _existedEdges[id] ){
				
				if(edge.fromNode.id == id)
					r.push(edge.toNode);
				else
					r.push(edge.fromNode);
			}
			return r;
		}
		public function changed():void
		{
			_changed = true;
			dispatchEvent(new Event(CHANGE));
		}
		public function getNode(name:String):Node
		{
			var n:Node = null;
			if(_existedEntityNodes.hasOwnProperty(name)){
				n = _existedEntityNodes[name];
			}
			else{
				n = _existedEntityNodes[name] = createNode(name);
			}

			return n;
		}
		private function createNode(name:String):Node
		{
			var n:Node = new Node(_existedNodeCount ++ ,name);
			
			if(_showAllNodes || name.charAt(0) != "\"")
				_nodeArray.push(n);
			
			_existedEdges[n.id] = new Object();
			_existedNodes[n.id] = n;
			
			return n;
		}
		
		public function set rdfData(rdf:NTData):void
		{
			_rdfData = rdf;
			initGraph();
		}
		public function isEmpty():Boolean
		{
			return _rdfData.isEmpty();
		}
		
		public function get edgeArray():Array
		{
			return _edgeArray;
		}
		public function get nodeArray():Array
		{
			return _nodeArray;
		}
		
		public function get layoutChanged():Boolean
		{
			return _changed;
		}
		public function set layoutChanged(c:Boolean):void
		{
			_changed = c;
		}
	}
	

}